/*******************************************************************************

                  LVGL 输入法之纯触模输入法实现

*******************************************************************************/
#include "lvIME.h"
#ifdef SUPPORT_LV_IME_TOUCH

#include "GB2312_Utf8.h"
#include "Unicode_Utf8.h"
#include "TMenuBase.h" //TMenu_ToTGuiKey
#include <string.h>

struct _lvIME_Clipboard lvIME_Clipboard;  //直接静态定义剪切板
//暂存最后一次输入法类型
static unsigned char _LastIME = LVIME_TYPE_U_LETTER;

static void _EventHandler(lv_event_t * e);//事件回调实现

//----------------------------接口输入法掩码得到内部掩码--------------------------
//该输入法可用,用原软键盘已删除时方可调用
//返回0成功，1不支持的输入法，负立即退出本输入法
static unsigned short _ToCurTypeMask(unsigned char InTypeMask,
                                     const char *pSignTbl)//挂接的符号表
{
  unsigned short TypeMask = InTypeMask & 0x1F; //低5项直接对应
  TypeMask |= (1 << LVIME_TYPE_SEL);//带选择器
  if(InTypeMask & (1 << LVIME_TYPE_PINYIN))
    TypeMask |= (1 << LVIME_TYPE_PINYIN9) | (1 << LVIME_TYPE_PINYIN26);
  if(pSignTbl != NULL)
    TypeMask |= (1 << LVIME_TYPE_CUSTOM);
  //T9与GUIDE只能二选一
  //if(!(InTypeMask & 0x80)){//最高强制禁止G9与GUIDE
    #if defined(SUPPORT_LV_IME_T9)
      TypeMask |= (1 << LVIME_TYPE_T9);
    #elif defined(SUPPORT_LV_IME_GUIDE)
      TypeMask |= (1 << LVIME_TYPE_GUIDE);
    #endif
  //}
  return TypeMask;
}

//----------------------------拼音输入完成回调函数实现----------------------------
//pCh为输入完成的字符(UTF8格式)
void _PinYin_EndCb(void *pv, char *pCh)
{
  struct _lvIME_Touch *pIME = (struct _lvIME_Touch *)pv;
  lv_textarea_add_text(pIME->pObj, pCh);
}

//----------------------------得到输入法选择掩码---------------------------------
//该输入法可用,用原软键盘已删除时方可调用
//返回0成功，1不支持的输入法，负立即退出本输入法
static unsigned short _GetSelDisMask(struct _lvIME_Touch *pIME)
{
  unsigned char TypeMask = pIME->TypeMask;
  //由前4项决定
  unsigned short DisMask = 0;
  if(!(TypeMask & (1 << LVIME_TYPE_PINYIN)))
    DisMask |= (1 << 6) | (1 << 7);
  if(!(TypeMask & (1 << LVIME_TYPE_NUM)))
    DisMask |= (1 << 2);
  if(!(TypeMask & (1 << LVIME_TYPE_L_LETTER)))
    DisMask |= (1 << 4);
  if(!(TypeMask & (1 << LVIME_TYPE_U_LETTER)))
    DisMask |= (1 << 3);
  if(!(TypeMask & (1 << LVIME_TYPE_SIGN)))
    DisMask |= (1 << 5);
  if(pIME->pSignTbl == NULL)
    DisMask |= (1 << 8);
  if(pIME->pString == NULL)//外部编辑禁T9或GUIDE
    DisMask |= (1 << 0);
  //T9与GUIDE只能二选一
  #if !defined(SUPPORT_LV_IME_T9) && !defined(SUPPORT_LV_IME_GUIDE)
    DisMask |= (1 << 0); //禁T9或GUIDE
  #endif
  return DisMask;
}

//---------------------------删除虚拟键盘---------------------------------
//该输入法可用,用原软键盘已删除时方可调用
//返回0成功，1不支持的输入法，负立即退出本输入法
static void _DeleteKb(struct _lvIME_Touch *pIME)
{
  //先删除附加虚拟键盘
  if((pIME->CurIME == LVIME_TYPE_PINYIN26) || (pIME->CurIME == LVIME_TYPE_PINYIN9))
    lvIME_PinYinDelete(&pIME->PY);
  //再删除软键盘自已
  if(pIME->pKeyG != NULL){
    lv_obj_del(pIME->pKeyG);
    pIME->pKeyG = NULL;
  }
}

//---------------------------创建虚拟键盘---------------------------------
//该输入法可用,用原软键盘已删除时方可调用
//返回0成功，1不支持的输入法，负立即退出本输入法
static signed char _CreateKb(struct _lvIME_Touch *pIME) //主结构
{
  signed char Type = pIME->CurIME;
  if(Type < 0) Type = _LastIME; //使用上次输入法
  switch(Type){
    case LVIME_TYPE_SEL:
      pIME->pKeyG = lvIME_CreateTypeSel(_EventHandler,
                                        pIME, _GetSelDisMask(pIME)); break;
    #if defined(SUPPORT_LV_IME_T9) || defined(SUPPORT_LV_IME_GUIDE)
    case LVIME_TYPE_PINYIN:
    case LVIME_TYPE_T9:
    case LVIME_TYPE_GUIDE:
      if(pIME->pString != NULL)//内部编辑模式时复制编辑好的字符串
        strcpy(pIME->pString, lv_textarea_get_text(pIME->pObj));
      lvIME_TouchDelete(pIME);
      lvIME_cbTouchToT9Guide(pIME);
      return -1; //立即退出本输入法
    #endif
    case LVIME_TYPE_NUM:
      pIME->pKeyG = lvIME_CreateNum(_EventHandler, pIME, 0); break;
    case LVIME_TYPE_U_LETTER:
      pIME->pKeyG = lvIME_CreateAbc(_EventHandler, pIME, 0); break;
    case LVIME_TYPE_L_LETTER:
      pIME->pKeyG = lvIME_CreateAbc(_EventHandler, pIME, 1); break;
    case LVIME_TYPE_SIGN:
      pIME->pKeyG = lvIME_CreateSign(_EventHandler, pIME, NULL); break;
    case LVIME_TYPE_PINYIN9:
      pIME->pKeyG = lvIME_CreateNum(_EventHandler, pIME, 1);
      lvIME_CreatePinYin(&pIME->PY,pIME->pKeyG, 0, _PinYin_EndCb, pIME);
       break;
    case LVIME_TYPE_PINYIN26:
      pIME->pKeyG = lvIME_CreateAbc(_EventHandler, pIME, 0);//大写键盘
      lvIME_CreatePinYin(&pIME->PY,pIME->pKeyG, 1, _PinYin_EndCb, pIME);
       break;
    case LVIME_TYPE_CUSTOM:
      pIME->pKeyG = lvIME_CreateSign(_EventHandler, pIME, pIME->pSignTbl); break;
    case LVIME_TYPE_IN_CODE:
      pIME->pKeyG = lvIME_CreateInCode(_EventHandler, pIME); break;
    default: Type = LVIME_TYPE_SEL;//异常回导航输入法
  }
  //后续处理
  _LastIME = Type; //记住最后输入法以方便使用
  return 0;
}

//----------------------下一输入法查找表--------------------------
static unsigned char _NextImeType[] = {
  LVIME_TYPE_PINYIN9,         //当前为TIME的输入法
  LVIME_TYPE_PINYIN9,         //7当前为数字输入
  LVIME_TYPE_U_LETTER,        //3当前为小写字母
  LVIME_TYPE_SIGN,            //4当前为大写字母
  LVIME_TYPE_CUSTOM,          //5当前为符号输入
  LVIME_TYPE_PINYIN9,         //当前为输入法选择键盘模式
  LVIME_TYPE_PINYIN9,         //当前为T9输入法,为完整输入法,用于选择
  LVIME_TYPE_PINYIN9,         //当前为导航输入法,为完整输入法,用于选择
  LVIME_TYPE_PINYIN26,        //1当前为9键拼音输入
  LVIME_TYPE_L_LETTER,        //2当前为26键拼音输入
  LVIME_TYPE_NUM,             //6当前为定制符号输入
};

//--------------------得到下个输入法-------------------------------
//返回下个输入法类型
signed char _GetNextIME(struct _lvIME_Touch *pIME)
{
  unsigned short TypeMask = pIME->TypeMask;
  unsigned char Type = pIME->CurIME;
  unsigned char NextType = _NextImeType[Type];
  while(NextType != Type){
    if(TypeMask & (1 << NextType)) return NextType;//支持了
    NextType = _NextImeType[NextType]; //下一个
  };
  return Type; //只有当前一种输入法
}

//----------------------类型选择键盘ID到输入类型查找表--------------------------
static unsigned char _TypeSel2ImeType[] = {
  #ifdef SUPPORT_LV_IME_T9
    LVIME_TYPE_T9,        //"中T9",
  #elif defined(SUPPORT_LV_IME_GUIDE)
    LVIME_TYPE_GUIDE,     //"中导航",
  #else //其它时，可定制用户当前键盘
    LVIME_TYPE_T9,        //"中T9",
  #endif
  LVIME_TYPE_IN_CODE,   //"内码输入",
  LVIME_TYPE_NUM,       //"123",
  LVIME_TYPE_U_LETTER,  //"ABC",
  LVIME_TYPE_L_LETTER,  //"abc",
  LVIME_TYPE_SIGN,      //"符Sign",
  LVIME_TYPE_PINYIN9,   //"拼音9",
  LVIME_TYPE_PINYIN26,  //"拼音26",
  LVIME_TYPE_CUSTOM,     //"符Custom"
};

//------------------------------剪切板处理------------------------------------
void _ClipboardPro(lv_obj_t *pTextArea, unsigned char Key)
{
  if(Key == LV_KEY_UP)//设置起始
    lvIME_Clipboard.Start = lv_textarea_get_cursor_pos(pTextArea);
  else if(Key == LV_KEY_ESC)//粘贴功能直接使用内建即可
    lv_textarea_add_text(pTextArea, lvIME_Clipboard.Content);
  else if((Key == LV_KEY_DOWN) || (Key == LV_KEY_ENTER)){//剪切或复制
    lv_obj_t *pLb = lv_textarea_get_label(pTextArea);
    //选择区优先
    unsigned short Start = lv_label_get_text_selection_start(pLb);
    unsigned short End = lv_label_get_text_selection_end(pLb);
    if(Start == End){//无选择区时，才以选择进行
      Start = lvIME_Clipboard.Start;
      if(Start == 0xffff) return; //未选择
      End = lv_textarea_get_cursor_pos(pTextArea);
    }
    if(Start == End) return; //起点与终点相同了
    //反了,先交换
    signed char IsSw;
    if(Start > End){unsigned short m = Start;  Start = End; End = m; IsSw = 1;}
    else IsSw = 0;
    //因是UTF8字符,需转换长度位置
    const char *pUtf8 = lv_textarea_get_text(pTextArea);
    unsigned long StrLen = strlen(pUtf8);
    unsigned long Need = Start;
    const char *pStartUtf8 = Utf8StrLen(pUtf8, StrLen, &Need);
    if(Need != Start) return;//异常
    Need = End - Start;
    const char *pEndUtf8 = Utf8StrLen(pStartUtf8, StrLen, &Need);
    if(Need != (End - Start)) return;//异常
    if(pEndUtf8 == NULL) pEndUtf8 = pUtf8 + StrLen;//结束位置
    unsigned long CopyLen = pEndUtf8 - pStartUtf8;
    if(CopyLen >= (LV_IME_CLIPBOARD_LEN - 1)) return; //长度超限
    memcpy(lvIME_Clipboard.Content, pStartUtf8, CopyLen);
    lvIME_Clipboard.Content[CopyLen] = '\0';//结束位置
    if(Key == LV_KEY_DOWN){//剪切时
      lv_label_cut_text(pLb, Start, End - Start);
      lv_label_set_text_selection_end(pLb, //取消选择区
                                      lv_label_get_text_selection_start(pLb));
      lv_textarea_set_cursor_pos(pTextArea, Start);//移动光标到起始
    }
    lvIME_Clipboard.Start = 0xffff;//操作完成，需重新设置起点
  }
}

//-------------------------------事件回调实现---------------------------------
static void _EventHandler(lv_event_t * e)
{
  lv_event_code_t Code = lv_event_get_code(e);
  lv_obj_t * obj = lv_event_get_target_obj(e);
  struct _lvIME_Touch *pIME = (struct _lvIME_Touch *)lv_event_get_user_data(e);
  lv_obj_t *pKeyG = pIME->pKeyG;
  if(Code != LV_EVENT_VALUE_CHANGED) return;
  unsigned char Key = 0;
  switch(pIME->CurIME){
    case LVIME_TYPE_SEL:
      Key = lvIME_GetKeyTypeSel(pKeyG);
      if(Key >= '0'){
        _DeleteKb(pIME); //删除软键盘自已
        pIME->CurIME = _TypeSel2ImeType[Key - '0'];
        _CreateKb(pIME); //对应输入法软键盘
        return;
      }
      //本地的按键处理
      if((Key == LV_KEY_LEFT) || (Key == LV_KEY_RIGHT) ||
         (Key == LV_KEY_BACKSPACE))  break;
      //剪切板处理
      _ClipboardPro(pIME->pObj, Key);
      return;
    case LVIME_TYPE_NUM:
      Key = lvIME_GetKeyNum(pKeyG); break;
    case LVIME_TYPE_U_LETTER:
    case LVIME_TYPE_L_LETTER:
      Key = lvIME_GetKeyAbc(pKeyG); break;
    case LVIME_TYPE_SIGN:
    case LVIME_TYPE_CUSTOM:
      Key = lvIME_GetKeySign(pKeyG);
      if(Key == LV_KEY_NEXT){//切换下一组符号
        lvIME_ToNextSign(pKeyG);
        return;
      }
      if(Key == LV_KEY_PREV){//选择符号类型
        //在当前键盘上的点击位置附近，弹出选择菜单
        lv_obj_t *pSignSelParent = lv_obj_create(LV_IME_KB_TOP_LAYER);
        lv_obj_remove_style_all(pSignSelParent); //去除圆角等
        lv_obj_set_flex_grow(pSignSelParent, 1);  //高可以伸展到全部空间
        lv_obj_set_style_border_width(pSignSelParent, 0, LV_PART_MAIN);  // 去边框
        lv_obj_set_style_outline_width(pSignSelParent, 0, LV_PART_MAIN); // 去边界
        lv_obj_set_size(pSignSelParent, 320, 320);//两排大概大小
        lv_obj_align(pSignSelParent, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
        lvIME_CreateSignSel(pSignSelParent, pKeyG);
        return;
      }
      if((Key < 'a') || (Key > 'y')) break;
      //选择对应符号了
      lv_textarea_add_text(pIME->pObj, lvIME_pGetSign(pKeyG, Key));
      return;
    case LVIME_TYPE_PINYIN9:
      Key = lvIME_GetKeyNum(pKeyG);
      //后退键在输入法退出时才对编辑区有效
      if(Key == LV_KEY_BACKSPACE){
        if(lvIME_PinYinKbKeyPro(&pIME->PY,Key) == 1) break;
        return;
      }
      if((Key < '0') || (Key > '9')) break;
      //9键拼音输入法处理
      lvIME_PinYinKbKeyPro(&pIME->PY,Key);
      return;
    case LVIME_TYPE_PINYIN26:
      Key = lvIME_GetKeyAbc(pKeyG);
      //后退键在输入法退出时才对编辑区有效
      if(Key == LV_KEY_BACKSPACE){
        if(lvIME_PinYinKbKeyPro(&pIME->PY,Key) == 1) break;
        return;
      }
      if((Key < 'A') || (Key > 'Z')) break;
      //26键拼音输入法处理
      lvIME_PinYinKbKeyPro(&pIME->PY,Key);
      return;
    case LVIME_TYPE_IN_CODE:{
      signed char Resume = lvIME_GetInCodeKey(pKeyG);
      if(Resume == 0) return; //内部处理完成
      if(Resume == -1){//内部处理完成，获得输入字符
        lv_textarea_add_text(pIME->pObj, lvIME_GetInCodeChar(pKeyG));
        return;
      }
      //其它时为系统键，继续处理
      Key = Resume;
      break;
    }
    default: return; //异常
  }
  //键值处理
  lv_obj_t *pTextArea = pIME->pObj;
  switch(Key){
    case LV_KEY_END: //顺切下一输入法
      _DeleteKb(pIME); //删除软键盘自已
      pIME->CurIME = _GetNextIME(pIME);
      _CreateKb(pIME);//创建软键盘
      return;
    case LV_KEY_HOME: //切输入法选择
      _DeleteKb(pIME); //删除软键盘自已
      pIME->CurIME = LVIME_TYPE_SEL;
      _CreateKb(pIME);//创建软键盘
      return;
    case LV_KEY_BACKSPACE: //删除光标前
      lv_textarea_delete_char(pTextArea);
      return;
    case LV_KEY_ESC: //不保存退出
      lvIME_TouchDelete(pIME); //删除自已
      if(pIME->pEndCb != NULL) pIME->pEndCb(pIME->pvEndCb, 0); //告知用户
      return;
    case LV_KEY_ENTER: //保存退出
      if(pIME->pString != NULL)//内部编辑模式时复制编辑好的字符串
        strcpy(pIME->pString, lv_textarea_get_text(pTextArea));
      lvIME_TouchDelete(pIME); //删除自已
      if(pIME->pEndCb != NULL) pIME->pEndCb(pIME->pvEndCb, 1); //告知用户
      return;
    case LV_KEY_LEFT: //光标左移
      lv_textarea_cursor_left(pTextArea);
      return;
    case LV_KEY_RIGHT: //光标左移
      lv_textarea_cursor_right(pTextArea);
      return;
    default: break;
  }
  //其它当做普通ASC字符插入处理
  if(Key < 0x20) return; //异常
  char String[2];
  String[0] = Key;
  String[1] = '\0';
  lv_textarea_add_text(pTextArea, String);
}

//----------------------------创建触模输入法函数---------------------------------
//本创建方式自带编辑器，支持与T9或TGuide切换，但不支持默认输入法为T9与导航类型
//返回是否成功初始化
signed char lvIME_TouchCreate(lv_obj_t * pParent,   //父窗口
                              struct _lvIME_Touch *pIME, //主结构
                              lvIME_EndCb_t pEndCb, //退出回调
                              void *pvEndCb,        //退出回调参数
                              char *pString,          //用户已存在的字符串,必须以'\0'结尾
                              unsigned short Size,     //字符串缓冲区允许大小+1
                              signed char DefaultType, //默认输入法,<0为上次
                              unsigned char TypeMask,//可使用的输入法类型
                              const char *pSignTbl, //挂接的符号表,NULL时无
                              unsigned short Cursor) //默认光标位置,-1时为最后
{
  #if defined(SUPPORT_LV_IME_T9) || defined(SUPPORT_LV_IME_GUIDE)
    if((DefaultType == LVIME_TYPE_T9) || (DefaultType == LVIME_TYPE_GUIDE))
      return 0;
  #endif
  //结构初始化
  memset(pIME, 0, sizeof(struct _lvIME_Touch));
  pIME->pEndCb = pEndCb;
  pIME->pvEndCb = pvEndCb;
  pIME->pString = pString;
  pIME->Size = Size;
  if(DefaultType < 0) pIME->CurIME = _LastIME; //使用最后一次选择的输入法
  else pIME->CurIME = DefaultType;
  pIME->TypeMask = _ToCurTypeMask(TypeMask, pSignTbl);
  pIME->pSignTbl = pSignTbl;

  //创建编辑区用于字符编辑
  lv_obj_t * pTextArea = lv_textarea_create(lv_screen_active());
  lv_obj_align(pTextArea, LV_ALIGN_TOP_MID, 0, 10);
  lv_textarea_set_one_line(pTextArea, true); //单行显示模式
  lv_textarea_set_max_length(pTextArea, Size);
  lv_textarea_set_text(pTextArea, pString);
  lv_textarea_set_cursor_pos(pTextArea, Cursor);
  #ifdef LV_IME_FONTS_TOUCH_EDIT  //单独指定编辑字体时
    lv_obj_set_style_text_font(pTextArea, &LV_IME_FONTS_TOUCH_EDIT, 0)
  #endif
  pIME->pObj = pTextArea;

  lv_textarea_set_text_selection(pTextArea, 1); //可选择文本以实现复制粘贴
  lvIME_Clipboard.Start = 0xffff; //表示未设置

  //进入当前可用输入法
  if(!(pIME->TypeMask & (1 << pIME->CurIME))) pIME->CurIME = _GetNextIME(pIME);
  _CreateKb(pIME);//创建软键盘

  return 1;
}

//-------------------------创建不带编辑区的触模输入法-----------------------------
//本模式带入用户创建好编辑区,并对编辑器区内容进行插入删除等操作。
//仅支持触模输入法，不支持TIME/TGUIDE输入法
void lvIME_TouchCreateTA(struct _lvIME_Touch *pIME, //未初始化的主结构
                         lvIME_EndCb_t pEndCb, //退出回调,可为NULL
                         void *pvEndCb,        //退出回调参数,可为NULL
                         lv_obj_t * pTextArea, //用户编辑区
                         signed char DefaultType, //默认输入法,0时拼音26
                         unsigned char TypeMask,//可使用的输入法类型,建议0XFF
                         const char *pSignTbl) //挂接的符号表,NULL时无
{
  if((DefaultType == LVIME_TYPE_T9) || (DefaultType == LVIME_TYPE_GUIDE))
    DefaultType = _LastIME;
  else if(DefaultType == LVIME_TYPE_PINYIN)
    DefaultType = LVIME_TYPE_PINYIN26;
  //结构初始化
  memset(pIME, 0, sizeof(struct _lvIME_Touch));
  pIME->pEndCb = pEndCb;
  pIME->pvEndCb = pvEndCb;
  pIME->pObj = pTextArea;
  pIME->pString = NULL; //标识为外部编辑
  pIME->Size = 0;
  if(DefaultType < 0) pIME->CurIME = _LastIME; //使用最后一次选择的输入法
  else pIME->CurIME = DefaultType;
  pIME->TypeMask = _ToCurTypeMask(TypeMask, pSignTbl);
  pIME->TypeMask &= ~((1 << LVIME_TYPE_T9) | (1 << LVIME_TYPE_GUIDE));
  pIME->pSignTbl = pSignTbl;

  lv_textarea_set_text_selection(pTextArea, 1); //可选择文本以实现复制粘贴
  lvIME_Clipboard.Start = 0xffff; //表示未设置

  //进入当前可用输入法
  if(!(pIME->TypeMask & (1 << pIME->CurIME))) pIME->CurIME = _GetNextIME(pIME);
  _CreateKb(pIME);//创建软键盘
}

//----------------------------删除自身---------------------------------
void lvIME_TouchDelete(struct _lvIME_Touch *pIME)
{
  //内建编辑器时，删除编辑器
  if((pIME->pString != NULL) && (pIME->pObj != NULL)){
    lv_obj_del(pIME->pObj);
    pIME->pObj = NULL;
  }
  _DeleteKb(pIME); //删除软键盘自已
}

#endif //#ifdef SUPPORT_LV_IME_TOUCH
